#include "driver_function.h"

void driver_rtc_init()
{
    uint8_t m_rtc_init_cnt = 0;
    uint16_t m_rtc_bkg;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
    PWR_BackupAccessCmd(ENABLE);

    m_rtc_bkg = BKP_ReadBackupRegister(BKP_DR1); // read backup DR1 regist
    if (m_rtc_bkg != 0xABAB) { //never set time ,need set time
        BKP_DeInit();
        RCC_LSEConfig(RCC_LSE_ON);
        while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET && m_rtc_init_cnt < 250) {
            m_rtc_init_cnt++;
            driver_timer_delay_us(10000);
        }
        if(m_rtc_init_cnt >= 250) {
            printf("driver rtc clock is error\r\n");
            return;
        }
        RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
        RCC_RTCCLKCmd(ENABLE);
        RTC_WaitForLastTask();
        RTC_WaitForSynchro();

        RTC_ITConfig(RTC_IT_ALR, ENABLE);
        RTC_WaitForLastTask();
        RTC_EnterConfigMode();
        RTC_SetPrescaler(32767);
        RTC_WaitForLastTask();
        driver_rtc_set_time(2025,9,18,16,25,13);
        RTC_ExitConfigMode();
        BKP_WriteBackupRegister(BKP_DR1, 0xABAB);

    } else { //have set ,not need set time again
        RCC_LSEConfig(RCC_LSE_ON);
        while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET && m_rtc_init_cnt < 250) {
            m_rtc_init_cnt++;
            driver_timer_delay_us(10000);
        }
        if(m_rtc_init_cnt >= 250) {
            printf("driver rtc clock is error\r\n");
            return;
        }
        RCC_RTCCLKCmd(ENABLE);
        RTC_WaitForLastTask();
        RTC_WaitForSynchro();
        RTC_ITConfig(RTC_IT_ALR, ENABLE);
        RTC_WaitForLastTask();
    }


    NVIC_InitTypeDef NVIC_InitStructure = {0};
    NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

uint8_t const driver_rtc_table_week[12] = {0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5};
const uint8_t driver_rtc_mon_table[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

static uint8_t driver_rtc_is_leap_year(u16 year)
{
    if(year % 4 == 0)
    {
        if(year % 100 == 0)
        {
            if(year % 400 == 0)
                return 1;
            else
                return 0;
        }
        else
            return 1;
    }
    else
        return 0;
}

uint8_t driver_rtc_set_time(u16 syear, u8 smon, u8 sday, u8 hour, u8 min, u8 sec)
{
    u16 t;
    u32 seccount = 0;
    if(syear < 1970 || syear > 2099) return 1; //year is too large

    for(t = 1970; t < syear; t++) {
        if( driver_rtc_is_leap_year(t) ) seccount += 31622400;
        else seccount += 31536000;
    }

    smon -= 1;
    for(t = 0; t < smon; t++){
        seccount += (u32)driver_rtc_mon_table[t] * 86400;
        if(driver_rtc_is_leap_year(syear) && t == 1) seccount += 86400;
    }

    seccount += (u32)(sday - 1) * 86400;
    seccount += (u32)hour * 3600;
    seccount += (u32)min * 60;
    seccount += sec;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
    PWR_BackupAccessCmd(ENABLE);
    RTC_SetCounter(seccount);
    RTC_WaitForLastTask();

    return 0;
}

uint8_t driver_rtc_set_alarm_time(u16 syear, u8 smon, u8 sday, u8 hour, u8 min, u8 sec)
{
    u16 t;
    u32 seccount = 0;
    if(syear < 1970 || syear > 2099) return 1;
    for(t = 1970; t < syear; t++) {
        if(driver_rtc_is_leap_year(t)) seccount += 31622400;
        else seccount += 31536000;
    }
    smon -= 1;
    for(t = 0; t < smon; t++) {
        seccount += (u32)driver_rtc_mon_table[t] * 86400;
        if(driver_rtc_is_leap_year(syear) && t == 1) seccount += 86400;
    }

    seccount += (u32)(sday - 1) * 86400;
    seccount += (u32)hour * 3600;
    seccount += (u32)min * 60;
    seccount += sec;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE);
    PWR_BackupAccessCmd(ENABLE);
    RTC_SetAlarm(seccount);
    RTC_WaitForLastTask();

    return 0;
}

uint8_t driver_rtc_get_week(u16 year, u8 month, u8 day)
{
    u16 temp2;
    u8  yearH, yearL;

    yearH = year / 100;
    yearL = year % 100;
    if(yearH > 19)
        yearL += 100;
    temp2 = yearL + yearL / 4;
    temp2 = temp2 % 7;
    temp2 = temp2 + day + driver_rtc_table_week[month - 1];
    if(yearL % 4 == 0 && month < 3)
        temp2--;
    return (temp2 % 7);
}

uint8_t driver_rtc_get_time(driver_rtc_obj *date)
{
    static u16 daycnt = 0;
    u32        timecount = 0;
    u32        temp = 0;
    u16        temp1 = 0;
    timecount = RTC_GetCounter();
    temp = timecount / 86400;
    if(daycnt != temp) {
        daycnt = temp;
        temp1 = 1970;
        while(temp >= 365) {
                if(driver_rtc_is_leap_year(temp1)) {
                    if(temp >= 366) temp -= 366;
                    else {
                        temp1++;
                        break;
                    }
                }
                else temp -= 365;
                temp1++;
            }
        date->time_year = temp1;
        temp1 = 0;

        while(temp >= 28) {
            if(driver_rtc_is_leap_year(date->time_year) && temp1 == 1) {
                if(temp >= 29) temp -= 29;
                else break;
            } else {
                    if(temp >= driver_rtc_mon_table[temp1]) temp -= driver_rtc_mon_table[temp1];
                    else break;
            }
            temp1++;
        }
        date->time_month = temp1 + 1;
        date->time_day = temp + 1;
    }

    temp = timecount % 86400;
    date->time_hour = temp / 3600;
    date->time_min = (temp % 3600) / 60;
    date->time_sec = (temp % 3600) % 60;
    date->time_week = driver_rtc_get_week(date->time_year, date->time_month, date->time_day);

    return 0;
}

void RTC_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
void RTC_IRQHandler(void)
{
    if(RTC_GetITStatus(RTC_IT_ALR) != RESET) /* Alarm clock interrupt */
    {
        RTC_ClearITPendingBit(RTC_IT_ALR);
    }

    RTC_ClearITPendingBit(RTC_IT_SEC | RTC_IT_OW);
    RTC_WaitForLastTask();
}
